package table;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;

import userInterface.GUIController;
import userInterface.MainPage;

import main.Main;

import data.AbstractDataType;
import data.DataMatcher;
import db_neo4j.Query_DB;

import field_REGEX_Definitions.Other.*;
import fields.*;
import fileProcessing.ParsingException;
import fileProcessing.ProjectDirectories;

public class Table {
	private String tablename;
	private String dataFilename = ProjectDirectories.dataFileDir+"\\";
	private String collectionInfoFilename = ProjectDirectories.dataFileDir+"\\";
	private boolean hasHeading;//how file is structured, has header line or not
	private int recordCount = 0;
	private ArrayList<AbstractField> fields = new ArrayList<AbstractField>();
	private ArrayList<Integer> matchLength = new ArrayList<Integer>();
	private RandomAccessFile dataFile;
	private RandomAccessFile collectionData;
	private FileCollectionData sourceLocation;
	private FileCollectionData collectionDate;
	
	public Table(String name){
		tablename = name;
		dataFilename+=(tablename+"_Records");
		collectionInfoFilename+=(tablename+"_CollectionInfo");
		hasHeading = false;
	}
	
	public Table(String name, ArrayList<AbstractField> fieldsIn){
		tablename = name;
		dataFilename+=(tablename+"_Records");
		collectionInfoFilename+=(tablename+"_CollectionInfo");
		for(AbstractField f : fieldsIn)
			addField(f);
		hasHeading = true;
	}
	
	public String getTableName(){
		return tablename;
	}
	
	public void addFileCollectionData(String line) throws ParsingException{
		URL_Source source = new URL_Source();
		Collection_Date date = new Collection_Date();
		try {
			collectionData = new RandomAccessFile(collectionInfoFilename,"rw");
			
			if(source.matches(line)){
				collectionData.seek(collectionData.length());
				sourceLocation = source.execute();
				sourceLocation.saveContent(collectionData);
			}
			else if(date.matches(line)){
				collectionData.seek(collectionData.length());
				collectionDate = date.execute();
				collectionDate.saveContent(collectionData);
			}
			else throw new ParsingException("Could not locate the file's collection source and date.");
			
			collectionData.close();
			
		} catch (FileNotFoundException e) {
			throw new ParsingException("Could not open CollectionInfo Data file");
		} catch (IOException e1) {
			throw new ParsingException("Problems seeking within file");
		}
	}
	
	/**
	 * Add data from the file to create a new record.
	 * First check to see if the fields array contains any fields. If it does then parse
	 * the line against the fields 
	 * @param data
	 * @throws ParsingException 
	 */
	public void addData(String[] data) throws ParsingException{
		int counter = 0;
		try {
			dataFile = new RandomAccessFile(dataFilename,"rw");
				if(hasHeading){
					if(data.length == fields.size()){//if number of tokens matches the number of fields
						for(AbstractField field : fields){
							//System.out.println("Add data for loop 1");
							dataFile.seek(dataFile.length());
							field.addData(dataFile,data[counter]);
							counter++;
						}
						recordCount++;
					}
					else{//more data tokens than fields
						if(data.length>fields.size()){
							//System.out.println("About to try to fit");
							String[] conditionedData = fitFieldData(data);
							System.out.println("fitted data returned");
							System.out.println(fields.size());
							counter = 0;
							for(AbstractField field : fields){
								System.out.println("Add data for loop 2");
								dataFile.seek(dataFile.length());
								System.out.println("seek completed. Adding "+conditionedData[counter]+" for field "+field.getFieldName());
								field.addData(dataFile,conditionedData[counter]);
								counter++;
							}
							recordCount++;
						}
					}
				}
				else{
					if(data.length%fields.size() == 0){
						for(String d : data){
							dataFile.seek(dataFile.length());
							try{
							fields.get(counter%fields.size()).addData(dataFile,d);
							} catch(Exception e){
								System.out.println((counter%fields.size())+" "+data.length);
							}
							counter++;
						}
						recordCount+=(counter/fields.size());
					}
				}
			dataFile.close();
		} catch (FileNotFoundException e) {
			throw new ParsingException("Could not open Records file");
		} catch (IOException e1) {
			throw new ParsingException("Problems seeking within file");
		}
	}
	
	public void addField(AbstractField newField){
		fields.add(newField);
	}
	
	public int getRecordCount(){
		return recordCount;
	}
	
	public void delete() {
		(new File(dataFilename)).delete();
	}

	public String getTableInfo() {
		// TODO Auto-generated method stub
		return null;
	}

	public void toCSV(BufferedWriter bw,String delimiter) throws ParsingException{		
		try {
			String output = "";
			dataFile = new RandomAccessFile(dataFilename,"rw");
			int tempIndex=0;
			for(AbstractField f : fields){
				if(tempIndex == fields.size()-1)
					output+=(f.getFieldName()+"\r\n");
				else
					output+=(f.getFieldName()+delimiter);
				tempIndex++;
			}
			bw.write(output);
			bw.flush();
			
			String currentValue;
			for(int i=1;i<=recordCount;i++){
				for(AbstractField f : fields){
					currentValue = f.getValue(dataFile).toString();	
					System.out.println(currentValue);
					bw.write(currentValue +delimiter);
				}
				bw.write("\r\n");
				System.out.println("record count: "+i);
			}
			
			bw.flush();
			dataFile.close();
			
			if(Main.usingDB){
				System.out.println("Finished writing table contents, adding to database...");
				toGraphDB();
				System.out.println("Querying database...");
				//new Query_DB(Main.db.getDB());
			}
			
		} catch (FileNotFoundException e) {
			throw new ParsingException("Error opening data file when displaying values.");
		} catch (IOException e) {
			throw new ParsingException("Error reading data to display.");
		} 
	}
	
	private void toGraphDB() throws ParsingException{
		Transaction tx = Main.db.graphDb.beginTx();
		Node tableNode,recordNode;
		
		try{
			collectionData = new RandomAccessFile(collectionInfoFilename,"rw");
			String sLocation = sourceLocation.getContent(collectionData);
			String sDate = collectionDate.getContent(collectionData);	
			collectionData.close();
			
			//create new node for the overall source
			tableNode = Main.db.graphDb.createNode();
			tableNode.setProperty(sourceLocation.getFieldName(),sLocation);
			tableNode.setProperty(collectionDate.getFieldName(),sDate);
			
			dataFile = new RandomAccessFile(dataFilename,"rw");
			String currentValue;
			for(int i=1;i<=recordCount;i++){	
				recordNode = Main.db.graphDb.createNode();
				for(AbstractField f : fields){
					currentValue = f.getValue(dataFile).toString();
					//add node property
					recordNode.setProperty(f.getFieldName(), currentValue);
				}
				recordNode.createRelationshipTo(tableNode, Main.db.getRelationship());
			}
			dataFile.close();
			//System.out.println("finished adding to db");
			tx.success();
		} catch (IOException e) {
			throw new ParsingException(e.getMessage());
		} finally {
			tx.finish();
			
	}
		
	}
	
	/**
	 * Take the raw line of data to parse as a record and determine the most likely configuration
	 * so that the return array fits the number of fields with the correct data identified for 
	 * each field.
	 * @param data
	 * @return
	 */
	public String[] fitFieldData(String[] data) throws ParsingException{
		int sizeWanted = fields.size();
		int currentSize = data.length;
		String[] result = new String[sizeWanted];
		
		System.out.println("Trying to fit data");
		int[] matchTracker = new int[fields.size()];//array to track which input tokens have been assigned to the output.
		
		if((currentSize-sizeWanted) == 0)
			return data;
		else{//determine the possible number of fields expected
			ArrayList<String> dataHolder = new ArrayList<String>();
			//String dataHolder[] = new String[fields.size()];
			//iterate through the fields and determine if specific words match the required structure if present
			int counter = 0;
			int counter2 = 0;
			for(AbstractField f : fields){
				//System.out.println("fitting data for loop 1");
				matchTracker[counter] = -1;
				for(String s : data){
					//System.out.println("fitting data for loop 2");
					if(f.verify(s) == 1){
						dataHolder.add(s);
						matchTracker[counter] = counter2; 
						break;
					}
					counter2++;
				}
				if(dataHolder.size() < counter)
					dataHolder.add("");
				
				counter++;
			}
			
			/*
			 * At this point all direct matches have been made.  In the next block determine how many
			 * tokens are left from the input to be matched
			 */
			
			counter = 0; 
			counter2 = 0;
			
			for(int i : matchTracker){
				if((i>-1) && (counter>0)){
					//if current field is matched then determine matches for field[counter-1]
					//matches for previous field are based on available tokens prior to the 
					//current position and after the last matched field.
					
					counter2 = 0;
				}
				else if(i == -1){
					counter2++;
				}
			}
			counter = 0;
			//if((data.length - dataHolder.size())%2 == 0)
			for(String s : dataHolder){
				//System.out.println("fitting data for loop 3");
				if(s.equalsIgnoreCase("")){
					if(dataHolder.contains(data[counter])){
						counter++;
						dataHolder.add(dataHolder.indexOf(s), data[counter]);
					}
				}					
				counter++;
			}
			
			counter = 0;
			for(String s : dataHolder){
				if(s == null)
					result[counter] = "";
				else
					result[counter] = s;
				counter++;
			}
			return result;
		}
	}
}
